/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.transas.zorlog.data.log;

import com.transas.zorlog.data.*;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * @author amalikov
 *
 * Creates map of id/PacketParser from given logformat xml
 *
 */
public class Config {
	
	public Config(Reader in) {
		this.in = in;
		versionMap = new TreeMap<>();
	}

	/**
	 * Reads given logformat xml file, creates PacketParsers
	 * @param in - xml file
	 */
	public void read() {
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(new InputSource(in));

			readLogType(doc.getDocumentElement());
			
			versionMap.clear();
			NodeList nodes = doc.getDocumentElement().getElementsByTagName("version");
		
			for (int i = 0; i < nodes.getLength(); ++i) {
				Node vnode = nodes.item(i);
				Version version = new Version();
				readTimeConfig(vnode, version);
				createParsers(vnode, version);
				versionMap.put(
						Integer.parseInt(vnode.getAttributes().getNamedItem("value").getNodeValue()), 
						version);
			}

        } catch (ParserConfigurationException | SAXException | IOException ex) {
			Logger.getLogger(Config.class.getName()).log(Level.SEVERE, null, ex);
		}
	}
	
	/**
	 * Returns names of all fields
	 */
	public List<String> getFieldNames(int version) {
		List<String> names = new ArrayList<>();
		Map<Integer, PacketParser> parserMap = versionMap.get(version).parserMap;
		
		for (PacketParser p : parserMap.values())
			for (Field f : p.getFields())
				names.add(f.name);
		
		return names;
	}
	
	/**
	 * Returns parserMap
	 */
	public Map<Integer, PacketParser> getParserMap(int version) {
		return versionMap.get(version).parserMap;
	}


	public TimeSource createTimeSource(DataSource ds, int version) {
		Version v = versionMap.get(version);

		TimeFormatter fmt = "hms".equals(v.timeFormat) ? new TimeFormatterHMS() : new TimeFormatterSimple();
		TimeSource ts = "".equals(v.timeValue) ? 
				new TimeSourceFromIndex(v.timeScale, fmt, v.playbackRate) : 
				new TimeSourceFromData(ds.getValues(v.timeValue), v.timeScale, fmt, v.playbackRate);
		return ts;
	}

	public float getPlaybackRate(int version) {
		return versionMap.get(version).playbackRate;
	}
	
	
	/**
	 * Parses info tag
	 */
	private void readLogType(Node root) {
		Node node = ((Element)root).getElementsByTagName("info").item(0);
		Node typeNode = node.getAttributes().getNamedItem("type");
		if (typeNode != null)
			logType = typeNode.getNodeValue();
	}
	
	
	/**
	 * Parses timeline tag
	 */
	private void readTimeConfig(Node root, Version version) {
		Node node = ((Element)root).getElementsByTagName("timeline").item(0);
		NamedNodeMap attrs = node.getAttributes();
			
		Node valueNode = attrs.getNamedItem("value");
		if (valueNode != null)
			version.timeValue = valueNode.getNodeValue();
			
		Node scaleNode = attrs.getNamedItem("scale");
		if (scaleNode != null)
			version.timeScale = Double.parseDouble(scaleNode.getNodeValue());
			
		Node formatNode = attrs.getNamedItem("format");
		if (formatNode != null)
			version.timeFormat = formatNode.getNodeValue();
		
		Node playbackNode = attrs.getNamedItem("playback");
		if (playbackNode != null)
			version.playbackRate = Float.parseFloat(playbackNode.getNodeValue());
	}
	
	/**
	 * Creates PacketParser objects by given xml root node. 
	 * Expects tags "packet" with attribute "id".
	 * @param root - xml root node.
	 */
	private void createParsers(Node root, Version version) {
		NodeList nodes = ((Element)root).getElementsByTagName("packet");
		
		for (int i = 0; i < nodes.getLength(); ++i) {
			NamedNodeMap attrs = nodes.item(i).getAttributes();
			int id = Integer.parseInt(attrs.getNamedItem("id").getNodeValue());
			boolean master = (i == 0);
			version.parserMap.put(id, new PacketParser(createFields(nodes.item(i)), master));
		}
	}

	/**
	 * Returns Field objects by given xml "packet" node.
	 * Expects tags "field" with attributes "offset", "type", "name", "scale".
	 * @param item - xml "packet" node.
	 */
	private List<Field> createFields(Node item) {
		List<Field> fields = new ArrayList<>();
		NodeList nodes = ((Element)item).getElementsByTagName("field");
			
		for (int i = 0; i < nodes.getLength(); ++i) {
			NamedNodeMap attrs = nodes.item(i).getAttributes();
			Node offset = attrs.getNamedItem("offset");
			Node type = attrs.getNamedItem("type");
			Node name = attrs.getNamedItem("name");
			Node scale = attrs.getNamedItem("scale");
			Node precision = attrs.getNamedItem("precision");
			Node bitnum = attrs.getNamedItem("bit");
			
			if (offset != null && type != null && name != null) {
				fields.add(Field.create(
						Integer.parseInt(offset.getNodeValue()),
						type.getNodeValue(),
						name.getNodeValue(),
						((scale != null) ? Double.parseDouble(scale.getNodeValue()) : 1.0),
						((precision != null) ? Integer.parseInt(precision.getNodeValue()) : 0),
						((bitnum != null) ? Integer.parseInt(bitnum.getNodeValue()) : -1)));
			}
		}
	
		return fields;
	}

	private Reader in;

	private class Version {
		public Map<Integer, PacketParser> parserMap = new TreeMap<>();
		public String timeValue = "";
		public double timeScale = 1;
		public String timeFormat = "simple";
		public float playbackRate = 0.05f;
	}
	
	private String logType = "fcslog";
	
	private Map<Integer, Version> versionMap;
}
